App = {}

App.apply = function(o, c, defaults) {
	// no "this" reference for friendly out of scope calls
	if (defaults) {
		App.apply(o, defaults);
	}
	if (o && c && typeof c == 'object') {
		for (var p in c) {
			o[p] = c[p];
		}
	}
	return o;
};


(function() {
	var idSeed = 0, toString = Object.prototype.toString, ua = navigator.userAgent
			.toLowerCase(), check = function(r) {
		return r.test(ua);
	}, DOC = document, docMode = DOC.documentMode, isStrict = DOC.compatMode == "CSS1Compat", isOpera = check(/opera/), isChrome = check(/\bchrome\b/), isWebKit = check(/webkit/), isSafari = !isChrome
			&& check(/safari/), isSafari2 = isSafari && check(/applewebkit\/4/), // unique
	// to
	// Safari
	// 2
	isSafari3 = isSafari && check(/version\/3/), isSafari4 = isSafari
			&& check(/version\/4/), isIE = !isOpera && check(/msie/), isIE7 = isIE
			&& (check(/msie 7/) || docMode == 7), isIE8 = isIE
			&& (check(/msie 8/) && docMode != 7), isIE9 = isIE
			&& check(/msie 9/), isIE6 = isIE && !isIE7 && !isIE8 && !isIE9, isGecko = !isWebKit
			&& check(/gecko/), isGecko2 = isGecko && check(/rv:1\.8/), isGecko3 = isGecko
			&& check(/rv:1\.9/), isBorderBox = isIE && !isStrict, isWindows = check(/windows|win32/), isMac = check(/macintosh|mac os x/), isAir = check(/adobeair/), isLinux = check(/linux/), isSecure = /^https/i
			.test(window.location.protocol);

	// remove css image flicker
	if (isIE6) {
		try {
			DOC.execCommand("BackgroundImageCache", false, true);
		} catch (e) {
		}
	}

	App.apply(App, {
		/**
		 * URL to a blank file used by App when in secure mode for iframe src
		 * and onReady src to prevent the IE insecure content warning (<tt>'about:blank'</tt>,
		 * except for IE in secure mode, which is <tt>'javascript:""'</tt>).
		 * 
		 * @type String
		 */
		SSL_SECURE_URL : isSecure && isIE ? 'javascript:""' : 'about:blank',
		/**
		 * True if the browser is in strict (standards-compliant) mode, as
		 * opposed to quirks mode
		 * 
		 * @type Boolean
		 */
		isStrict : isStrict,
		/**
		 * True if the page is running over SSL
		 * 
		 * @type Boolean
		 */
		isSecure : isSecure,

		/**
		 * Indicates whether to use native browser parsing for JSON methods.
		 * This option is ignored if the browser does not support native JSON
		 * methods. <b>Note: Native JSON methods will not work with objects that
		 * have functions. Also, property names must be quoted, otherwise the
		 * data will not parse.</b> (Defaults to false)
		 * 
		 * @type Boolean
		 */
		USE_NATIVE_JSON : false,

		/**
		 * Copies all the properties of config to obj if they don't already
		 * exist.
		 * 
		 * @param {Object}
		 *            obj The receiver of the properties
		 * @param {Object}
		 *            config The source of the properties
		 * @return {Object} returns obj
		 */
		applyIf : function(o, c) {
			if (o) {
				for (var p in c) {
					if (!App.isDefined(o[p])) {
						o[p] = c[p];
					}
				}
			}
			return o;
		},
		isIterable : function(v) {
			// check for array or arguments
			if (App.isArray(v) || v.callee) {
				return true;
			}
			// check for node list type
			if (/NodeList|HTMLCollection/.test(toString.call(v))) {
				return true;
			}
			// NodeList has an item and length property
			// IXMLDOMNodeList has nextNode method, needs to be checked first.
			return ((typeof v.nextNode != 'undefined' || v.item) && Ext
					.isNumber(v.length));
		},

		/**
		 * Iterates an array calling the supplied function.
		 * 
		 * @param {Array/NodeList/Mixed}
		 *            array The array to be iterated. If this argument is not
		 *            really an array, the supplied function is called once.
		 * @param {Function}
		 *            fn The function to be called with each item. If the
		 *            supplied function returns false, iteration stops and this
		 *            method returns the current <code>index</code>. This
		 *            function is called with the following arguments: <div
		 *            class="mdetail-params">
		 *            <ul>
		 *            <li><code>item</code> : <i>Mixed</i> <div
		 *            class="sub-desc">The item at the current
		 *            <code>index</code> in the passed <code>array</code></div></li>
		 *            <li><code>index</code> : <i>Number</i> <div
		 *            class="sub-desc">The current index within the array</div></li>
		 *            <li><code>allItems</code> : <i>Array</i> <div
		 *            class="sub-desc">The <code>array</code> passed as the
		 *            first argument to <code>App.each</code>.</div></li>
		 *            </ul>
		 *            </div>
		 * @param {Object}
		 *            scope The scope (<code>this</code> reference) in which
		 *            the specified function is executed. Defaults to the
		 *            <code>item</code> at the current <code>index</code>
		 *            within the passed <code>array</code>.
		 * @return See description for the fn parameter.
		 */
		each : function(array, fn, scope) {
			if (App.isEmpty(array, true)) {
				return;
			}
			if (!App.isIterable(array) || App.isPrimitive(array)) {
				array = [array];
			}
			for (var i = 0, len = array.length; i < len; i++) {
				if (fn.call(scope || array[i], array[i], i, array) === false) {
					return i;
				};
			}
		},

		/**
		 * Iterates either the elements in an array, or each of the properties
		 * in an object. <b>Note</b>: If you are only iterating arrays, it is
		 * better to call {@link #each}.
		 * 
		 * @param {Object/Array}
		 *            object The object or array to be iterated
		 * @param {Function}
		 *            fn The function to be called for each iteration. The
		 *            iteration will stop if the supplied function returns
		 *            false, or all array elements / object properties have been
		 *            covered. The signature varies depending on the type of
		 *            object being interated: <div class="mdetail-params">
		 *            <ul>
		 *            <li>Arrays :
		 *            <tt>(Object item, Number index, Array allItems)</tt>
		 *            <div class="sub-desc"> When iterating an array, the
		 *            supplied function is called with each item.</div></li>
		 *            <li>Objects : <tt>(String key, Object value, Object)</tt>
		 *            <div class="sub-desc"> When iterating an object, the
		 *            supplied function is called with each key-value pair in
		 *            the object, and the iterated object</div></li>
		 *            </ul>
		 *            </div>
		 * @param {Object}
		 *            scope The scope (<code>this</code> reference) in which
		 *            the specified function is executed. Defaults to the
		 *            <code>object</code> being iterated.
		 */
		iterate : function(obj, fn, scope) {
			if (App.isEmpty(obj)) {
				return;
			}
			if (App.isIterable(obj)) {
				App.each(obj, fn, scope);
				return;
			} else if (typeof obj == 'object') {
				for (var prop in obj) {
					if (obj.hasOwnProperty(prop)) {
						if (fn.call(scope || obj, prop, obj[prop], obj) === false) {
							return;
						};
					}
				}
			}
		},

		/**
		 * Return the dom node for the passed String (id), dom node, or
		 * App.Element. Optional 'strict' flag is needed for IE since it can
		 * return 'name' and 'id' elements by using getElementById. Here are
		 * some examples:
		 * 
		 * <pre><code>
		 * 		// gets dom node based on id
		 * 		var elDom = App.getDom('elId');
		 * 		// gets dom node based on the dom node
		 * 		var elDom1 = App.getDom(elDom);
		 * 
		 * 		// If we don 't know if we are working with an
		 * 		// App.Element or a dom node use App.getDom
		 * 		function(el){
		 * 		var dom = App.getDom(el);
		 * 		// do something with the dom node
		 * 		}
		 * </code></pre>
		 * 
		 * <b>Note</b>: the dom node to be found actually needs to exist (be
		 * rendered, etc) when this method is called to be successful.
		 * 
		 * @param {Mixed}
		 *            el
		 * @return HTMLElement
		 */
		getDom : function(el, strict) {
			if (!el || !DOC) {
				return null;
			}
			if (el.dom) {
				return el.dom;
			} else {
				if (typeof el == 'string') {
					var e = DOC.getElementById(el);
					// IE returns elements with the 'name' and 'id' attribute.
					// we do a strict check to return the element with only the
					// id attribute
					if (e && isIE && strict) {
						if (el == e.getAttribute('id')) {
							return e;
						} else {
							return null;
						}
					}
					return e;
				} else {
					return el;
				}
			}
		},

		/**
		 * Creates namespaces to be used for scoping variables and classes so
		 * that they are not global. Specifying the last node of a namespace
		 * implicitly creates all other nodes. Usage:
		 * 
		 * <pre><code>
		 * 		App.namespace('Company', 'Company.data');
		 * 		App.namespace('Company.data'); // equivalent and preferable to above syntax
		 * 		Company.Widget = function() { ... }
		 * 		Company.data.CustomStore = function(config) { ... }
		 * 		
		 * </code></pre>
		 * 
		 * @param {String}
		 *            namespace1
		 * @param {String}
		 *            namespace2
		 * @param {String}
		 *            etc
		 * @return {Object} The namespace object. (If multiple arguments are
		 *         passed, this will be the last namespace created)
		 * @method namespace
		 */
		namespace : function() {
			var len1 = arguments.length, i = 0, len2, j, main, ns, sub, current;

			for (; i < len1; ++i) {
				main = arguments[i];
				ns = arguments[i].split('.');
				current = window[ns[0]];
				if (current === undefined) {
					current = window[ns[0]] = {};
				}
				sub = ns.slice(1);
				len2 = sub.length;
				for (j = 0; j < len2; ++j) {
					current = current[sub[j]] = current[sub[j]] || {};
				}
			}
			return current;
		},

		/**
		 * Takes an object and converts it to an encoded URL. e.g.
		 * App.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".
		 * Optionally, property values can be arrays, instead of keys and the
		 * resulting string that's returned will contain a name/value pair for
		 * each array value.
		 * 
		 * @param {Object}
		 *            o
		 * @param {String}
		 *            pre (optional) A prefix to add to the url encoded string
		 * @return {String}
		 */
		urlEncode : function(o, pre) {
			var empty, buf = [], e = encodeURIComponent;

			App.iterate(o, function(key, item) {
				empty = App.isEmpty(item);
				App.each(empty ? key : item, function(val) {
							buf
									.push(
											'&',
											e(key),
											'=',
											(!App.isEmpty(val) && (val != key || !empty))
													? (App.isDate(val)
															? App
																	.encode(val)
																	.replace(
																			/"/g,
																			'')
															: e(val))
													: '');
						});
			});
			if (!pre) {
				buf.shift();
				pre = '';
			}
			return pre + buf.join('');
		},

		/**
		 * Takes an encoded URL and and converts it to an object. Example:
		 * 
		 * <pre><code>
		 * App.urlDecode(&quot;foo=1&amp;bar=2&quot;); // returns {foo: &quot;1&quot;, bar: &quot;2&quot;}
		 * App.urlDecode(&quot;foo=1&amp;bar=2&amp;bar=3&amp;bar=4&quot;, false); // returns {foo: &quot;1&quot;, bar: [&quot;2&quot;, &quot;3&quot;, &quot;4&quot;]}
		 * </code></pre>
		 * 
		 * @param {String}
		 *            string
		 * @param {Boolean}
		 *            overwrite (optional) Items of the same name will overwrite
		 *            previous values instead of creating an an array (Defaults
		 *            to false).
		 * @return {Object} A literal with members
		 */
		urlDecode : function(string, overwrite) {
			if (App.isEmpty(string)) {
				return {};
			}
			var obj = {}, pairs = string.split('&'), d = decodeURIComponent, name, value;
			App.each(pairs, function(pair) {
						pair = pair.split('=');
						name = d(pair[0]);
						value = d(pair[1]);
						obj[name] = overwrite || !obj[name] ? value : []
								.concat(obj[name]).concat(value);
					});
			return obj;
		},

		/**
		 * Appends content to the query string of a URL, handling logic for
		 * whether to place a question mark or ampersand.
		 * 
		 * @param {String}
		 *            url The URL to append to.
		 * @param {String}
		 *            s The content to append to the URL.
		 * @return (String) The resulting URL
		 */
		urlAppend : function(url, s) {
			if (!App.isEmpty(s)) {
				return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
			}
			return url;
		},

		/**
		 * Converts any iterable (numeric indices and a length property) into a
		 * true array Don't use this on strings. IE doesn't support "abc"[0]
		 * which this implementation depends on. For strings, use this instead:
		 * "abc".match(/./g) => [a,b,c];
		 * 
		 * @param {Iterable}
		 *            the iterable object to be turned into a true Array.
		 * @return (Array) array
		 */
		toArray : function() {
			return isIE ? function(a, i, j, res) {
				res = [];
				for (var x = 0, len = a.length; x < len; x++) {
					res.push(a[x]);
				}
				return res.slice(i || 0, j || res.length);
			} : function(a, i, j) {
				return Array.prototype.slice.call(a, i || 0, j || a.length);
			};
		}(),

		isIterable : function(v) {
			// check for array or arguments
			if (App.isArray(v) || v.callee) {
				return true;
			}
			// check for node list type
			if (/NodeList|HTMLCollection/.test(toString.call(v))) {
				return true;
			}
			// NodeList has an item and length property
			// IXMLDOMNodeList has nextNode method, needs to be checked first.
			return ((typeof v.nextNode != 'undefined' || v.item) && App
					.isNumber(v.length));
		},

		/**
		 * <p>
		 * Returns true if the passed value is empty.
		 * </p>
		 * <p>
		 * The value is deemed to be empty if it is<div class="mdetail-params">
		 * <ul>
		 * <li>null</li>
		 * <li>undefined</li>
		 * <li>an empty array</li>
		 * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter
		 * is <tt>true</tt>)</li>
		 * </ul>
		 * </div>
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @param {Boolean}
		 *            allowBlank (optional) true to allow empty strings
		 *            (defaults to false)
		 * @return {Boolean}
		 */
		isEmpty : function(v, allowBlank) {
			return v === null || v === undefined
					|| ((App.isArray(v) && !v.length))
					|| (!allowBlank ? v === '' : false);
		},

		/**
		 * Returns true if the passed value is a JavaScript array, otherwise
		 * false.
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @return {Boolean}
		 */
		isArray : function(v) {
			return toString.apply(v) === '[object Array]';
		},

		/**
		 * Returns true if the passed object is a JavaScript date object,
		 * otherwise false.
		 * 
		 * @param {Object}
		 *            object The object to test
		 * @return {Boolean}
		 */
		isDate : function(v) {
			return toString.apply(v) === '[object Date]';
		},

		/**
		 * Returns true if the passed value is a JavaScript Object, otherwise
		 * false.
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @return {Boolean}
		 */
		isObject : function(v) {
			return !!v
					&& Object.prototype.toString.call(v) === '[object Object]';
		},

		/**
		 * Returns true if the passed value is a JavaScript 'primitive', a
		 * string, number or boolean.
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @return {Boolean}
		 */
		isPrimitive : function(v) {
			return App.isString(v) || App.isNumber(v) || App.isBoolean(v);
		},

		/**
		 * Returns true if the passed value is a JavaScript Function, otherwise
		 * false.
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @return {Boolean}
		 */
		isFunction : function(v) {
			return toString.apply(v) === '[object Function]';
		},

		/**
		 * Returns true if the passed value is a number. Returns false for
		 * non-finite numbers.
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @return {Boolean}
		 */
		isNumber : function(v) {
			return typeof v === 'number' && isFinite(v);
		},

		/**
		 * Returns true if the passed value is a string.
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @return {Boolean}
		 */
		isString : function(v) {
			return typeof v === 'string';
		},

		/**
		 * Returns true if the passed value is a boolean.
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @return {Boolean}
		 */
		isBoolean : function(v) {
			return typeof v === 'boolean';
		},

		/**
		 * Returns true if the passed value is an HTMLElement
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @return {Boolean}
		 */
		isElement : function(v) {
			return v ? !!v.tagName : false;
		},

		/**
		 * Returns true if the passed value is not undefined.
		 * 
		 * @param {Mixed}
		 *            value The value to test
		 * @return {Boolean}
		 */
		isDefined : function(v) {
			return typeof v !== 'undefined';
		},

		/**
		 * True if the detected browser is Opera.
		 * 
		 * @type Boolean
		 */
		isOpera : isOpera,
		/**
		 * True if the detected browser uses WebKit.
		 * 
		 * @type Boolean
		 */
		isWebKit : isWebKit,
		/**
		 * True if the detected browser is Chrome.
		 * 
		 * @type Boolean
		 */
		isChrome : isChrome,
		/**
		 * True if the detected browser is Safari.
		 * 
		 * @type Boolean
		 */
		isSafari : isSafari,
		/**
		 * True if the detected browser is Safari 3.x.
		 * 
		 * @type Boolean
		 */
		isSafari3 : isSafari3,
		/**
		 * True if the detected browser is Safari 4.x.
		 * 
		 * @type Boolean
		 */
		isSafari4 : isSafari4,
		/**
		 * True if the detected browser is Safari 2.x.
		 * 
		 * @type Boolean
		 */
		isSafari2 : isSafari2,
		/**
		 * True if the detected browser is Internet Explorer.
		 * 
		 * @type Boolean
		 */
		isIE : isIE,
		/**
		 * True if the detected browser is Internet Explorer 6.x.
		 * 
		 * @type Boolean
		 */
		isIE6 : isIE6,
		/**
		 * True if the detected browser is Internet Explorer 7.x.
		 * 
		 * @type Boolean
		 */
		isIE7 : isIE7,
		/**
		 * True if the detected browser is Internet Explorer 8.x.
		 * 
		 * @type Boolean
		 */
		isIE8 : isIE8,
		/**
		 * True if the detected browser is Internet Explorer 9.x.
		 * 
		 * @type Boolean
		 */
		isIE9 : isIE9,
		/**
		 * True if the detected browser uses the Gecko layout engine (e.g.
		 * Mozilla, Firefox).
		 * 
		 * @type Boolean
		 */
		isGecko : isGecko,
		/**
		 * True if the detected browser uses a pre-Gecko 1.9 layout engine (e.g.
		 * Firefox 2.x).
		 * 
		 * @type Boolean
		 */
		isGecko2 : isGecko2,
		/**
		 * True if the detected browser uses a Gecko 1.9+ layout engine (e.g.
		 * Firefox 3.x).
		 * 
		 * @type Boolean
		 */
		isGecko3 : isGecko3,
		/**
		 * True if the detected browser is Internet Explorer running in
		 * non-strict mode.
		 * 
		 * @type Boolean
		 */
		isBorderBox : isBorderBox,
		/**
		 * True if the detected platform is Linux.
		 * 
		 * @type Boolean
		 */
		isLinux : isLinux,
		/**
		 * True if the detected platform is Windows.
		 * 
		 * @type Boolean
		 */
		isWindows : isWindows,
		/**
		 * True if the detected platform is Mac OS.
		 * 
		 * @type Boolean
		 */
		isMac : isMac,
		/**
		 * True if the detected platform is Adobe Air.
		 * 
		 * @type Boolean
		 */
		isAir : isAir
	});

	App.ns = App.namespace;
})();

App.ns('App.util');

/**
 * @class String These functions are available on every String object.
 */
App.applyIf(String, {
			/**
			 * Allows you to define a tokenized string and pass an arbitrary
			 * number of arguments to replace the tokens. Each token must be
			 * unique, and must increment in the format {0}, {1}, etc. Example
			 * usage:
			 * 
			 * <pre><code>
			 * var cls = 'my-class', text = 'Some text';
			 * var s = String.format('&lt;div class=&quot;{0}&quot;&gt;{1}&lt;/div&gt;', cls, text);
			 * // s now contains the string: '&lt;div class=&quot;my-class&quot;&gt;Some text&lt;/div&gt;'
			 * </code></pre>
			 * 
			 * @param {String}
			 *            string The tokenized string to be formatted
			 * @param {String}
			 *            value1 The value to replace token {0}
			 * @param {String}
			 *            value2 Etc...
			 * @return {String} The formatted string
			 * @static
			 */
			format : function(format) {
				var args = App.toArray(arguments, 1);
				return format.replace(/\{(\d+)\}/g, function(m, i) {
							return args[i];
						});
			}
		});

/**
 * @class Array
 */
App.applyIf(Array.prototype, {
			/**
			 * Checks whether or not the specified object exists in the array.
			 * 
			 * @param {Object}
			 *            o The object to check for
			 * @param {Number}
			 *            from (Optional) The index at which to begin the search
			 * @return {Number} The index of o in the array (or -1 if it is not
			 *         found)
			 */
			indexOf : function(o, from) {
				var len = this.length;
				from = from || 0;
				from += (from < 0) ? len : 0;
				for (; from < len; ++from) {
					if (this[from] === o) {
						return from;
					}
				}
				return -1;
			},

			/**
			 * Removes the specified object from the array. If the object is not
			 * found nothing happens.
			 * 
			 * @param {Object}
			 *            o The object to remove
			 * @return {Array} this array
			 */
			remove : function(o) {
				var index = this.indexOf(o);
				if (index != -1) {
					this.splice(index, 1);
				}
				return this;
			}
		});

/**
 * @class App.util.TaskRunner Provides the ability to execute one or more
 *        arbitrary tasks in a multithreaded manner. Generally, you can use the
 *        singleton {@link App.TaskMgr} instead, but if needed, you can create
 *        separate instances of TaskRunner. Any number of separate tasks can be
 *        started at any time and will run independently of each other. Example
 *        usage:
 * 
 * <pre><code>
 * // Start a simple clock task that updates a div once per second
 * var updateClock = function() {
 * 	App.fly('clock').update(new Date().format('g:i:s A'));
 * }
 * var task = {
 * 	run : updateClock,
 * 	interval : 1000
 * 	//1 second
 * }
 * var runner = new App.util.TaskRunner();
 * runner.start(task);
 * 
 * // equivalent using TaskMgr
 * App.TaskMgr.start({
 * 			run : updateClock,
 * 			interval : 1000
 * 		});
 * </code></pre>
 * 
 * <p>
 * See the {@link #start} method for details about how to configure a task
 * object.
 * </p>
 * Also see {@link App.util.DelayedTask}.
 * 
 * @constructor
 * @param {Number}
 *            interval (optional) The minimum precision in milliseconds
 *            supported by this TaskRunner instance (defaults to 10)
 */
App.util.TaskRunner = function(interval) {
	interval = interval || 10;
	var tasks = [], removeQueue = [], id = 0, running = false,

	// private
	stopThread = function() {
		running = false;
		clearInterval(id);
		id = 0;
	},

	// private
	startThread = function() {
		if (!running) {
			running = true;
			id = setInterval(runTasks, interval);
		}
	},

	// private
	removeTask = function(t) {
		removeQueue.push(t);
		if (t.onStop) {
			t.onStop.apply(t.scope || t);
		}
	},

	// private
	runTasks = function() {
		var rqLen = removeQueue.length, now = new Date().getTime();

		if (rqLen > 0) {
			for (var i = 0; i < rqLen; i++) {
				tasks.remove(removeQueue[i]);
			}
			removeQueue = [];
			if (tasks.length < 1) {
				stopThread();
				return;
			}
		}
		for (var i = 0, t, itime, rt, len = tasks.length; i < len; ++i) {
			t = tasks[i];
			itime = now - t.taskRunTime;
			if (t.interval <= itime) {
				rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
				t.taskRunTime = now;
				if (rt === false || t.taskRunCount === t.repeat) {
					removeTask(t);
					return;
				}
			}
			if (t.duration && t.duration <= (now - t.taskStartTime)) {
				removeTask(t);
			}
		}
	};

	/**
	 * Starts a new task.
	 * 
	 * @method start
	 * @param {Object}
	 *            task
	 *            <p>
	 *            A config object that supports the following properties:
	 *            <ul>
	 *            <li><code>run</code> : Function<div class="sub-desc">
	 *            <p>
	 *            The function to execute each time the task is invoked. The
	 *            function will be called at each interval and passed the
	 *            <code>args</code> argument if specified, and the current
	 *            invocation count if not.
	 *            </p>
	 *            <p>
	 *            If a particular scope (<code>this</code> reference) is
	 *            required, be sure to specify it using the <code>scope</code>
	 *            argument.
	 *            </p>
	 *            <p>
	 *            Return <code>false</code> from this function to terminate
	 *            the task.
	 *            </p>
	 *            </div></li>
	 *            <li><code>interval</code> : Number<div
	 *            class="sub-desc">The frequency in milliseconds with which the
	 *            task should be invoked.</div></li>
	 *            <li><code>args</code> : Array<div
	 *            class="sub-desc">(optional) An array of arguments to be passed
	 *            to the function specified by <code>run</code>. If not
	 *            specified, the current invocation count is passed.</div></li>
	 *            <li><code>scope</code> : Object<div
	 *            class="sub-desc">(optional) The scope (<tt>this</tt>
	 *            reference) in which to execute the <code>run</code>
	 *            function. Defaults to the task config object.</div></li>
	 *            <li><code>duration</code> : Number<div
	 *            class="sub-desc">(optional) The length of time in milliseconds
	 *            to invoke the task before stopping automatically (defaults to
	 *            indefinite).</div></li>
	 *            <li><code>repeat</code> : Number<div
	 *            class="sub-desc">(optional) The number of times to invoke the
	 *            task before stopping automatically (defaults to indefinite).</div></li>
	 *            </ul>
	 *            </p>
	 *            <p>
	 *            Before each invocation, App injects the property
	 *            <code>taskRunCount</code> into the task object so that
	 *            calculations based on the repeat count can be performed.
	 *            </p>
	 * @return {Object} The task
	 */
	this.start = function(task) {
		tasks.push(task);
		task.taskStartTime = new Date().getTime();
		task.taskRunTime = 0;
		task.taskRunCount = 0;
		startThread();
		return task;
	};

	/**
	 * Stops an existing running task.
	 * 
	 * @method stop
	 * @param {Object}
	 *            task The task to stop
	 * @return {Object} The task
	 */
	this.stop = function(task) {
		removeTask(task);
		return task;
	};

	/**
	 * Stops all tasks that are currently running.
	 * 
	 * @method stopAll
	 */
	this.stopAll = function() {
		stopThread();
		for (var i = 0, len = tasks.length; i < len; i++) {
			if (tasks[i].onStop) {
				tasks[i].onStop();
			}
		}
		tasks = [];
		removeQueue = [];
	};
};

App.ns('App.common');

// web project js global object
App.common.Page = function(gopts) {
	var self = this;

	var defaultSettings = {
		formId : 'searchForm',
		pageNo : 1,
		pageSize : 15
	}

	self.settings = $.extend(true, {}, defaultSettings, gopts);

	/**
	 * 页面跳转方法,
	 * 
	 * 在同页面带有多个分页列表时,只保存当前查询表单分页状态,默认可在App.settings.page中配置 { formId:
	 * 'searchForm', *(必须指定) 当前列表查询表单ID,默认为searchForm, pageNo: 1, 当前页 pageSize:
	 * 15 页结果数量 }
	 * 
	 * @param {Object}
	 *            options
	 */
	self.jump = function(options) {

		// 当前分页配置,传入配置覆盖默认同名配置项
		var opts = $.extend(true, {}, self.settings, options);

		var formId = opts.formId, pageNo = opts.pageNo, pageSize = opts.pageSize;

		var $form = $('#' + formId);
		var $pageNo = $('#' + formId + ' input[name=pageNo]');

		$pageNo.attr('value', pageNo);

		$form.submit();
	}
}

/**
 * 基于artDialog 5 的对话框
 */
App.MessageBox = (function(){
    var self = this;
    
    
    return {
        /**
         * 动态加载对话框内容
         *
         * @param {Object}
         *            aid
         * @param {Object}
         *            title
         * @param {Object}
         *            url
         * @param {Object}
         *            okFun
         */
        load: function(aid, title, url, okFun){
            var ajax = new App.common.ajax();
            artDialog({
                id: aid,
                title: title,
                fixed: true,
                lock: true,
                ok: function(){
                    if (okFun) 
                        okFun();
                }
            });
            ajax.load(url, function(data){
                artDialog.get(aid).content(data);
            });
        },
        /**
         * 弹出对话框
         *
         * @param {Object}
         *            divId
         * @param {Object}
         *            dTitle
         * @param {Object}
         *            okCallback
         */
        show: function(divId, dTitle, okCallback){
            artDialog({
                id: divId,
                content: document.getElementById(divId),
                title: dTitle,
                fixed: true,
                lock: true,
                ok: function(){
                    if (okCallback) 
                        return okCallback();
                },
                cancel: true
            });
        },
        /**
         * 确认
         *
         * @param {Object}
         *            msg
         * @param {Object}
         *            okCallback
         */
        confirm: function(msg, onOk){
            artDialog({
                content: msg,
                fixed: true,
                lock: true,
                ok: function(){
					if(App.isFunction(onOk)){
						onOk.call(this,[]);
					}
                },
                cancel: true
            });
        },
        /**
         * 弹窗提示
         *
         * @param {Object}
         *            c
         */
        alert: function(c,beforeunload){
            artDialog({
                id: "Alert",
                fixed: !0,
                lock: !0,
                content: c,
                ok: !0,
                beforeunload: beforeunload
            });
        },
		prompt:  function(content,onOk,defaultValue){
			artDialog.prompt(content,onOk,defaultValue);
		}
    };
}());

App.Msg = App.MessageBox;



App.common.Form = function(gopts){
    
	var defaultOpts = {
		base:"/NONE/",
		/** 表示查询表单**/
		searchForm:'searchForm'
	};
	var opts = $.extend(true, {}, defaultOpts, gopts);
	
	
    return {
        /**
         * 通过一个checkbox来对多选框进行同步操作
         * @param {Object} name	check的名称
         * @param {Object} obj	触发的checkbox本身
         */
        checkToggle: function(name, obj){
            var b = obj.checked;
            var cs = document.getElementsByName(name);
            for (var i = 0; i < cs.length; i++) {
                cs[i].checked = b;
            }
        },
		getCheckboxValues:function(name){
			var cs=document.getElementsByName(name||'ids');
			var ids = [];
			for (var i=0;i<cs.length;i++) {
				var v = cs[i];
				if(v.checked){
					ids.push(v.value);
				}
			}
			return ids;
		},
		/**
		 * 自动渲染
		 * @param {Object} o	需要渲染对象
		 * @param {Object} prefix	id的前缀，根据此前缀+属性名得到doc对象
		 * @param {Object} kv	特殊属性名匹配
		 */
		render:function(o, prefix,kv){
			if(o){
				for(var k in o){
					$("#"+prefix+k).val(o[k]);
				}
			}
		},
		/**
		 * 渲染post对象
		 * @param {Object} o
		 * @param {Object} prefix
		 * @param {Object} kv
		 */
		renderPost:function(o, prefix, po_pre,kv){
			var po = {};
			if(o){
				for(var k in o){
					var obj = $("#"+prefix+k);
					if(obj) po[po_pre+k]=obj.val();
				}
			}
			return po;
		},
		/**
		 * 判断value是否为大于0的整数
		 * @param {Object} value
		 */
		gtZero:function(value){
			var n = parseInt(str);
			return !(str==''||isNaN(str)||n<=0);
		},
		/**
		 * 判断value是否为大于或者等于0的整数
		 * @param {Object} value
		 */
		gtEqZero:function(str){
			var n = parseInt(str);
			return !(str==''||isNaN(str)||n<0);
		},
		deleteRecord : function(ids,action,onSuccess){
			if(!ids||(App.isArray(ids)&&ids.length<1)){
				App.MessageBox.alert("请选择记录后再操作!");
				return;
			}
			App.MessageBox.confirm('是否确定删除?',function(){
				
				var url = String.format('{0}/{1}',opts.base,action||'delete.do');
				$.ajax({
					url: url,
					data:{
						'ids':ids
					},
					type:'post',
					dataType:'json',
					success:function(ret){
						if(onSuccess&&App.isFunction(onSuccess)){
							onSuccess.call(this,[ret]);
						}else{
							$(String.format('#{0}',opts.searchForm)).submit();
						}
					}
				});
				
			});
		}
    };
};

App.common.mask = function(id,msg){
	$(id||"body").mask(msg||"数据加载中...");
};
App.common.unmask = function(id){
	$(id||"body").unmask();
};


App.ns('App.network');

App.network.Ajax = (function(){

    var onSuccess = function(responseText){
		
	};
	
	var onError = function(){
		
	};
    return {
		send : function(url,params,successCall,errorCall){
		},
		/**
		 * 
		 * @param url
		 * @param params
		 * @param successCall
		 * @param errorCall
		 * @returns
		 */
		post:function(url,params,successCall,errorCall){
			$.post(ROOT_+url,params,function(data,stauts){
				if (stauts == "success") {
					successCall(data);
				}else{
					errorCall(data);
				}
			});
		},
		//以window.open 方式打开本地的一个新页面
		open:function(url,title,w,h){
			window.open(ROOT_+url,title,'height='+h+',width='+w);
		}
    };
}());

App.ajax = App.network.Ajax;

App.toast = (function(){
	return{
		success:function(m,t){
			toastr.success(t,m);
		},
		error:function(m,t){
			toastr.error(t,m);
		},
		info:function(m,t){
			toastr.info(t,m);
		},
		warning:function(m,t){
			toastr.warning(t,m);
		}
	}
}());



var ROOT_ = "/";
App.init = function(base){
    //定义一下artDialog的常量
    (function(d){
        d['okValue'] = '确定';
        d['cancelValue'] = '取消';
        d['title'] = '系统提醒你';
    })(artDialog.defaults);
    
    ROOT_ = base;
    $.ajaxSetup({
        traditional: true,
		timeout : 30000 ,
        beforeSend: function(){
            App.common.mask();
        },
        complete: function(){
            App.common.unmask();
        },
        error: function(xhr, status, e){
        
        }
    });
    
    //初始化 toastr
    toastr.options = {
	  "closeButton": false,
	  "debug": false,
	  "positionClass": "toast-top-full-width",
	  "onclick": null,
	  "showDuration": "300",
	  "hideDuration": "1000",
	  "timeOut": "10000",
	  "extendedTimeOut": "1000",
	  "showEasing": "linear",
	  "hideEasing": "linear",
	  "showMethod": "slideDown",
	  "hideMethod": "fadeOut"
	}
};
